package dispatcher

import (
	"encoding/binary"
	"lnzw/engine/log"
	"lnzw/engine/net/tcp"
	nw "lnzw/engine/net/work"
	w "lnzw/engine/work"
	"lnzw/server/logic/codemapping"
	"reflect"

	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
)

type dispatcher func(*tcp.SocketClient, []byte)

func ProtobufDispatcher(client *tcp.SocketClient, data []byte) {
	defer func() {
		if err := recover(); err != nil {
			log.Logger.Error("dispatcher panic", zap.Any("err", err))
		}
	}()
	codeBs := data[0:4]
	code := binary.BigEndian.Uint32(codeBs)
	ok, mapping := codemapping.GetMapping(int32(code))
	if !ok {
		log.Logger.Error("code not found", zap.Uint32("code", code))
		return
	}
	req := mapping.Req.ProtoReflect().New().Interface()
	res := mapping.Res.ProtoReflect().New().Interface()
	work := mapping.Work
	obj := reflect.New(reflect.ValueOf(work).Elem().Type()).Interface()
	err := proto.Unmarshal(data[4:], req)
	work = obj.(nw.IProtobufNetWork[proto.Message, proto.Message])
	if err != nil {
		log.Logger.Error("unmarshal failed", zap.Error(err))
		return
	}
	wr := reflect.ValueOf(work)
	wrt := reflect.TypeOf(work)
	runTagSet(obj, wr, wrt)

	w.Mgr.SubmitWork(work, client, int32(code), req, res)

}

// 这里有点硬编码的意思
// 目的是分发器在运行时动态设置work的run字段
func runTagSet(obj any, wr reflect.Value, wrt reflect.Type) {
	if wrt.Kind() == reflect.Ptr {
		wr = wr.Elem()
		wrt = wrt.Elem()
	}
	for i := 0; i < wrt.NumField(); i++ {
		//根据标签获得字段
		fieldName := wrt.Field(i).Tag.Get("work")
		if fieldName == "run" {
			wr.Field(i).Set(reflect.ValueOf(obj))
			return
		} else {
			if wrt.Field(i).Type.Kind() == reflect.Struct {
				runTagSet(obj, wr.Field(i), wrt.Field(i).Type)
			}
		}
	}
}

// 当前默认使用protobuf传输
var DefaultDispatcher dispatcher = ProtobufDispatcher
